package org.weed.remote.network.server;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;

public abstract class NioServer implements Runnable {
	protected static final int BUFFER_SIZE = 8192;
	protected InetAddress hostAddress;
	protected int port;
	
	protected ServerSocketChannel serverChannel;
	protected Selector selector;
	protected ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
	
	public NioServer(InetAddress hostAddress, int port) throws IOException {
		this.hostAddress = hostAddress;
		this.port = port;
		this.selector = this.initSelector();
	}
	
	protected Selector initSelector() throws IOException {
		Selector socketSelector = SelectorProvider.provider().openSelector();
		
		this.serverChannel = ServerSocketChannel.open();
		serverChannel.configureBlocking(false);
		
		InetSocketAddress socketAddress = new InetSocketAddress(hostAddress, port);
		serverChannel.socket().bind(socketAddress);
		
		serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
		
		return socketSelector;
	}
	
	public void run() {
		while (true) {
			try {
				selector.select();
				Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
				while (keys.hasNext()) {
					SelectionKey key = (SelectionKey) keys.next();
					keys.remove();
					if (key.isAcceptable()) {
						doAccept(key);
					} else if (key.isReadable()) {
						doRead(key);
					} else if (key.isWritable()) {
						doWrite(key);
					}
				}
				
			} catch (Exception e) {
				
			}
		}
	}
	
	protected void doAccept(SelectionKey key) throws IOException {
		ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();		
		SocketChannel socketChannel = serverSocketChannel.accept();
		socketChannel.configureBlocking(false);
		socketChannel.register(selector, SelectionKey.OP_READ);
	}
	
	abstract void doRead(SelectionKey key) throws IOException;
	
	abstract void doWrite(SelectionKey key) throws IOException;
}
